<!DOCTYPE html>
<html>
<head>
<title>Tic Tac Toe Task</title>
<!-- credit to flaticon.com for the icons used in this task. -->
<!-- stylesheets -->
<link rel="stylesheet" type="text/css" href="../core/core.css">
<!-- JS -->
<script src="../core/core.js"></script>
<script src="../core/jquery-ui/external/jquery/jquery.js"></script>

<style>
#area { margin-top: 10px; margin-left: 10px; }
.ttt-row { width: 150px; display: block; }
.ttt-row span { width: 40px; height: 40px; display: inline-block; border: 1px solid black; }
.mark-x { content:url(../common/special/tic-tac-toe/x.png); }
.mark-o { content:url(../common/special/tic-tac-toe/o.png); }
</style>

<script>
var TTT_TEMPLATE =
`
<div class="ttt-row">
  <span id="ttt-0" data-index="0"></span>
  <span id="ttt-1" data-index="1"></span>
  <span id="ttt-2" data-index="2"></span>
</div>
<div class="ttt-row">
  <span id="ttt-3" data-index="3"></span>
  <span id="ttt-4" data-index="4"></span>
  <span id="ttt-5" data-index="5"></span>
</div>
<div class="ttt-row">
  <span id="ttt-6" data-index="6"></span>
  <span id="ttt-7" data-index="7"></span>
  <span id="ttt-8" data-index="8"></span>
</div>
`

var MARKER_VALUE = {'mark-x': 1, 'mark-o': -1}
var INITIAL_GAME_STATE = [0,0,0, 0,0,0, 0,0,0];
var EMPTY_SPACES = [0, 1, 2, 3, 4, 5, 6, 7, 8];

// 55% chance to choose a random space, 45% chance to do a smart move.
var RANDOM_CHANCE = 55;

var gameState;
var freeSpaces;

var checkWinner = function(markerVal){
  // horizontally
  return (gameState[0] === markerVal && gameState[1] === markerVal && gameState[2] === markerVal)
    || (gameState[3] === markerVal && gameState[4] === markerVal && gameState[5] === markerVal)
    || (gameState[6] === markerVal && gameState[7] === markerVal && gameState[8] === markerVal)
    // vertically
    || (gameState[0] === markerVal && gameState[3] === markerVal && gameState[6] === markerVal)
    || (gameState[1] === markerVal && gameState[4] === markerVal && gameState[7] === markerVal)
    || (gameState[2] === markerVal && gameState[5] === markerVal && gameState[8] === markerVal)
    // diagonally
    || (gameState[0] === markerVal && gameState[4] === markerVal && gameState[8] === markerVal)
    || (gameState[2] === markerVal && gameState[4] === markerVal && gameState[6] === markerVal)
}

// hacky algorithm used to semi-intelligently check for a winning spot, and if not,
// find a spot that will prevent the agent from getting a win.
var determineSpace = function(enemyMarkerVal, markerVal){
  // first try to find something that lets them win.
  var winningMove = [checkLeftHorizontal(enemyMarkerVal), checkMiddleHorizontal(enemyMarkerVal),
    checkRightHorizontal(enemyMarkerVal), checkDiagonal(enemyMarkerVal), checkLeftVertical(enemyMarkerVal),
    checkMiddleVertical(enemyMarkerVal), checkRightVertical(enemyMarkerVal)]
      .filter(function(val){ return val !== false; }).pop();
  // also compute what moves would block a win from the opponent.
  var blockLosingMove = [checkLeftHorizontal(markerVal), checkMiddleHorizontal(markerVal),
    checkRightHorizontal(markerVal), checkDiagonal(markerVal), checkLeftVertical(markerVal),
    checkMiddleVertical(markerVal), checkRightVertical(markerVal)]
      .filter(function(val){ return val !== false; }).pop();

  // if there's a winning move, return that. if there's no winning move, return a move
  // that would block the opponent.
  return (winningMove >= 0 && winningMove !== undefined) ? winningMove : blockLosingMove;
}

var checkLeftHorizontal = function(marker){
  for(var i=0;i<3;i++){
    var currentSpace = gameState[i*3];
    if(currentSpace === 0 && gameState[i*3+1] === marker && gameState[i*3+2] === marker)
      return i*3;
  }
  return false;
}

var checkMiddleHorizontal = function(marker){
  for(var i=0;i<3;i++){
    var currentSpace = gameState[i*3+1];
    if(currentSpace === 0 && gameState[i*3] === marker && gameState[i*3+2] === marker)
      return i*3+1;
  }
  return false;
}

var checkRightHorizontal = function(marker){
  for(var i=0;i<3;i++){
    var currentSpace = gameState[i*3+2];
    if(currentSpace === 0 && gameState[i*3] === marker && gameState[i*3+1] === marker)
      return i*3+2;
  }
  return false;
}

var checkLeftVertical = function(marker){
  if(gameState[0] === 0 && gameState[3] === marker && gameState[6] === marker) return 0;
  else if (gameState[3] === 0 && gameState[0] === marker && gameState[6] === marker) return 3;
  else if(gameState[6] === 0 && gameState[0] === marker && gameState[3] === marker) return 6;
  return false;
}

var checkMiddleVertical = function(marker){
  if(gameState[1] === 0 && gameState[4] === marker && gameState[7] === marker) return 1;
  else if (gameState[4] === 0 && gameState[1] === marker && gameState[7] === marker) return 4;
  else if(gameState[7] === 0 && gameState[1] === marker && gameState[4] === marker) return 7;
  return false;
}

var checkRightVertical = function(marker){
  if(gameState[2] === 0 && gameState[5] === marker && gameState[8] === marker) return 2;
  else if (gameState[5] === 0 && gameState[2] === marker && gameState[8] === marker) return 5;
  else if(gameState[8] === 0 && gameState[2] === marker && gameState[5] === marker) return 8;
  return false;
}

var checkDiagonal = function(marker){
  var point = false;
  if(gameState[0] === 0 && gameState[4] === marker && gameState[8] === marker) point = 0;
  else if(centerDescending = gameState[4] === 0 && gameState[0] === marker && gameState[8] === marker) point = 4;
  else if(bottomRight = gameState[8] === 0 && gameState[0] === marker && gameState[4] === marker) point = 8;
  else if(gameState[2] === 0 && gameState[4] === marker && gameState[6] === marker) point = 2;
  else if(centerAscending = gameState[4] === 0 && gameState[2] === marker && gameState[6] === marker) point = 4;
  else if(bottomLeft = gameState[6] === 0 && gameState[2] === marker && gameState[4] === marker) point = 6;
  return point;
}

var computerMove = function(playerPiece, playerVal, enemyPiece, enemyVal){
  if(core.randi(0,100) < RANDOM_CHANCE || freeSpaces.length === 9){
    var enemySpace = core.sample(freeSpaces);
  } else {
    // otherwise, try to find a move that will win, or block a win.
    // if that fails, pick a random spot.
    var enemySpace = determineSpace(enemyVal, playerVal);
    if(enemySpace === undefined || enemySpace === false) enemySpace = core.sample(freeSpaces);
  }
  gameState[enemySpace] = enemyVal;
  freeSpaces.splice(freeSpaces.indexOf(enemySpace), 1);
  $('#ttt-' + enemySpace).addClass(enemyPiece);

  // if computer wins, give negative reward to agent. also check
  // if computer cauesd a draw, since it's possible for them to go first.
  if(checkWinner(enemyVal) === true){ callLoss(); }
  else if(freeSpaces.length === 0){ callDraw(); }
}

var callWin = function(){ core.endEpisode(1.0, true); }
var callDraw = function(){ core.endEpisode(-0.5, false); }
var callLoss = function(){ core.endEpisode(-0.75, false); }

var genProblem = function() {
  $('#area').empty();
  $('#area').append(TTT_TEMPLATE);
  gameState = INITIAL_GAME_STATE.slice();
  freeSpaces = EMPTY_SPACES.slice();

  var playerPiece = 'mark-x';
  var playerVal = 1;
  var queryText = 'X';
  var enemyPiece = 'mark-o';
  var enemyVal = -1;

  // 50% chance for the computer to start first.
  if(core.randi(0,100) > 50){ computerMove(playerPiece, playerVal, enemyPiece, enemyVal); }

  $('.ttt-row span').unbind('click');
  $('.ttt-row span').on('click', function(){
    var $this = $(this);
    if($this.attr('class') !== undefined){ return false; }

    $this.addClass(playerPiece);
    var elemIndex = parseInt($this.attr('data-index'), 10);
    gameState[elemIndex] = playerVal;
    freeSpaces.splice(freeSpaces.indexOf(elemIndex), 1);

    // first check if the player won, before having the opponent choose something.
    if(checkWinner(playerVal) === true){ callWin(); }
    else if(freeSpaces.length === 0){ callDraw(); }
    else { computerMove(playerPiece, playerVal, enemyPiece, enemyVal); }
  });
}

window.onload = function() {
  core.startEpisode();
}
</script>
</head>
<body>
<div id="wrap">
  <div id="query">Playing as 'X', win a game of tic-tac-toe.</div>
  <div id="area"></div>
</div>
</body>
</html>
